﻿using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;

using Utils;
using Utils.Buffer;

using static Microsoft.AspNetCore.Mvc.AuthAttribute;

namespace Microsoft.AspNetCore.Mvc
{
    public static class ControllerHelper
    {
        public static Verify<T> Verify<T>(this ControllerBase controllerBase, T obj)
        {
            return new Verify<T>(obj);
        }


        public static bool IsToken<T>(this ControllerBase controllerBase) where T : AuthIdentity
        {
            return controllerBase.HttpContext.User.Identity.AuthenticationType == typeof(T).Name;
        }

        public static AuthIdentity GetUserToken(this ControllerBase controllerBase)
        {
            return GetUserToken<AuthIdentity>(controllerBase);
        }


        public static T GetUserToken<T>(this ControllerBase controllerBase) where T : AuthIdentity
        {
            if (controllerBase.User.Identity is T res)
            {
                return res;
            }
            return null;
        }

        /// <summary>
        /// 权限判断
        /// </summary>
        /// <param name="controllerBase"></param>
        /// <param name="rightCode"></param>
        /// <param name="rightKey"></param>
        /// <returns></returns>
        public static bool IsInRight(this ControllerBase controllerBase, string rightCode, RightKey rightKey)
        {
            return controllerBase.GetUserToken().IsInRight(rightCode, rightKey);
        }


        public static string GetAuthSecret(this HttpContext httpContext)
        {
            var configuration = httpContext.RequestServices.GetRequiredService<IConfiguration>();
            return configuration["AuthSecret"] ?? "Mic.Core";
        }
        public static string CreartTicket(this ControllerBase controllerBase, AuthIdentity userToken, long expired = 3600 * 24 * 7)
        {
            var key = GetAuthSecret(controllerBase.HttpContext);
            var bs = userToken.Serialize();
            var bytes = ByteWriteBuffer.Alloc(bs.Length + 12 + userToken.GetType().Name.Length)
              .Write(expired + DateTime.Now.ToUnixTimestamp()) // 过期时间
              .WriteUnknownString(userToken.GetType().Name, Encoding.ASCII) // 解码对象名称
              .Write(bs);
            return AESEncrypt(bytes.ToBytes(), key);
        }
        /// <summary>
        /// 解析票据
        /// </summary>
        /// <param name="httpRequest"></param>
        /// <returns></returns>
        public static AuthorizationData GetAuthorizationData(this HttpRequest httpRequest)
        {
            try
            {
                string authorization = GetToken(httpRequest);
                if (string.IsNullOrEmpty(authorization))
                    return null;

                var key = httpRequest.HttpContext.GetAuthSecret();
                var buffer = ByteReadBuffer.Wrap(AESDecrypt(authorization, key));
                long expiryTime = buffer.ReadLong();
                string name = buffer.ReadUnknownString();
                var bytes = buffer.ReadEnd();
                return new AuthorizationData
                {
                    ExpiryTime = expiryTime,
                    TypeName = name,
                    Data = bytes
                };
            }
            catch (Exception)
            {
                return null;
            }
        }


        public static T GetAuth<T>(this HttpRequest httpRequest) where T : AuthIdentity, new()
        {
            var authorization = GetAuthorizationData(httpRequest);
            if (authorization == null) return default;
            if (typeof(T).Name.Equals(authorization.TypeName))
            {
                var token = new T();
                if (token.Deserialize(authorization.Data, httpRequest.HttpContext))
                {
                    return token;
                }
            }
            return null;
        }


        /// <summary>
        /// 获得服务器的ip地址
        /// </summary>
        /// <returns>ip地址</returns>
        public static string GetServerIP(this HttpRequest httpRequest)
        {
            if (httpRequest.Headers.TryGetValue("Local_Addr", out Microsoft.Extensions.Primitives.StringValues value))
            {
                return value.FirstOrDefault();
            }
            return "127.0.0.1";
        }


        /// <summary>
        /// 获得客户端的ip地址
        /// </summary>
        /// <returns>ip地址</returns>
        public static string GetClientIP(this HttpRequest httpRequest)
        {
            try
            {
                if (httpRequest.Headers.TryGetValue("X-Forwarded-For", out Extensions.Primitives.StringValues value) && value.Count > 0)
                {
                    return value.FirstOrDefault();
                }
                return httpRequest.HttpContext.Connection.RemoteIpAddress.ToString();
            }
            catch (Exception)
            {
                return "0.0.0.0";
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private static string GetToken(HttpRequest request)
        {
            if (request.Query.ContainsKey("access_token"))
            {
                return request.Query["access_token"];
            }
            if (request.Headers.ContainsKey("access-token"))
            {
                return request.Headers["access-token"].ToString();
            }
            if (request.Headers.ContainsKey("Authorization"))
            {
                return request.Headers["Authorization"].ToString().Replace("Bearer ", "");
            }
            if (request.Cookies.ContainsKey("access-token"))
            {
                return request.Cookies["access-token"];
            }
            return null;
        }
        /// <summary>
        /// ASE 加密 ECB
        /// </summary> 
        /// <returns></returns>
        private static string AESEncrypt(byte[] encryptStr, string AesKey)
        {
            byte[] keyArray = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(AesKey), keyArray, Math.Min(AesKey.Length, 32));
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(encryptStr, 0, encryptStr.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }


        /// <summary>
        /// AES 解密
        /// </summary>
        /// <param name="decryptStr"></param>
        /// <param name="AesKey"></param>
        /// <returns></returns>
        private static byte[] AESDecrypt(string decryptStr, string AesKey)
        {
            byte[] keyArray = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(AesKey), keyArray, Math.Min(AesKey.Length, 32));
            byte[] toEncryptArray = Convert.FromBase64String(decryptStr);
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateDecryptor();
            return cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

        }

    }
}
